package com.esunny.online.controller;

import com.esunny.online.annotation.OperationLogAnnotation;
import com.esunny.online.domain.dto.ResourceListQuery;
import com.esunny.online.domain.dto.ReturnStructure;
import com.esunny.online.domain.entity.Resource;
import com.esunny.online.domain.entity.User;
import com.esunny.online.service.ResourceService;
import com.esunny.online.util.*;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiResponse;
import org.apache.commons.lang3.StringUtils;
import org.bytedeco.javacv.FrameGrabber;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


/**
 * @author ：李先生
 * @date ：Created in 2020/7/16 15:21
 * @description： 与资源相关的控制器
 * @modified By：
 * @version: $
 */
@Api(value = "资源请求控制器")
@Controller
@RequestMapping("/resource")
public class ResourceController {

    /**
     * 域名
     */
    @Value("${online.path.domain}")
    private String domain;

    /**
     * 核心路径
     */
    @Value("${server.servlet.context-path}")
    private String contextPath;
    /**
     * 文件上传路径
     */
    @Value("${online.path.upload}")
    private String uploadPath;

    /**
     * resourceService
     */
    @javax.annotation.Resource
    private ResourceService resourceService;

    @javax.annotation.Resource
    private HostHolder hostHolder;

    /**
     * 日志
     */
    private Logger logger = LoggerFactory.getLogger(ResourceController.class);

    /**
     * 查询未通过审核资源数据
     * @param resourceListQuery 查询条件
     * @return JSON
     */
    @ApiOperation(value = "查询未通过审核资源数据")
    @ApiImplicitParam(paramType = "body", name = "resourceListQuery", value = "查询条件", required = true, dataType = "ResourceListQuery")
    @ApiResponse(code = 400, message = "参数错误", response = ReturnStructure.class)
    @ResponseBody
    @RequestMapping("/queryUncheckResource")
    public ReturnStructure queryUncheckResource(@RequestBody ResourceListQuery resourceListQuery){
        int code;
        String message;
        Map<String, Object> map = new HashMap<>(0);

        List<Resource> resources = resourceService.selectUncheckResource(resourceListQuery);
        code = 200;
        message = "获取成功";
        map.put("resourceList",resources);

        return new ReturnStructure(code,message,map);
    }

    /**
     * 查询未通过审核资源总数
     * @param resourceListQuery 查询条件
     * @return JSON
     */
    @ApiOperation(value = "查询未通过审核资源数据总数")
    @ApiImplicitParam(paramType = "body", name = "resourceListQuery", value = "查询条件", required = true, dataType = "ResourceListQuery")
    @ApiResponse(code = 400, message = "参数错误", response = ReturnStructure.class)
    @ResponseBody
    @RequestMapping("/queryUncheckCount")
    public ReturnStructure queryUncheckCount(@RequestBody ResourceListQuery resourceListQuery){
        int code;
        String message;
        Map<String, Object> map = new HashMap<>(0);

        int i = resourceService.selectUncheckCount(resourceListQuery);
        code = 200;
        message = "获取成功";
        map.put("count",i);

        return new ReturnStructure(code,message,map);
    }

    /**
     * 通过id更新数据
     * @param resource
     * @return JSON
     */
    @OperationLogAnnotation(url = "/resource/updateResource",modul = "资源模块",type = "修改",description = "通过id更新资源信息")
    @ApiOperation(value = "通过id更新数据")
    @ApiImplicitParam(paramType = "body", name = "resource", value = "查询条件", required = true, dataType = "Resource")
    @ApiResponse(code = 400, message = "参数错误", response = ReturnStructure.class)
    @ResponseBody
    @RequestMapping("/updateResource")
    public ReturnStructure updateResource(@RequestBody Resource resource){
        int code;
        String message;
        Map<String, Object> map = new HashMap<>(0);

        // 获取用户信息
        User user = hostHolder.getUser();
        boolean result = resourceService.updateResource(resource, user);
        if (result){
            code = 200;
            message = "修改成功";
        }else{
            code = 404;
            message = "修改失败";
        }
        return new ReturnStructure(code,message,map);
    }


    /**
     * 请求上传文件
     *
     * @param file 文件
     * @return JSON
     */
    @OperationLogAnnotation(url = "/resource/uploadFile",modul = "资源模块",type = "上传",description = "上传资源文件")
    @ApiOperation(value = "上传文件接口")
    @ApiImplicitParam(paramType = "body", name = "file", value = "文件", required = true, dataType = "MultipartFile")
    @ApiResponse(code = 400, message = "参数错误", response = ReturnStructure.class)
    @PostMapping("/uploadFile")
    @ResponseBody
    @CrossOrigin
    public ReturnStructure uploadFile(MultipartFile file) throws FrameGrabber.Exception {
        String method = "/uploadFile：";
        String pdf = ".pdf";
        if (file == null) {
            logger.warn(ConstantUtil.LOGGER_CONTROLLER_RESOURCE + method + "请求错误，文件为空");
            return new ReturnStructure(400, "请求错误，文件为空");
        } else {
            Resource resource = new Resource();
            //获取文件名
            String filename = file.getOriginalFilename();
            String filesize = String.valueOf(file.getSize());
            //获取文件后缀
            String suffix = "";
            if (null != filename) {
                suffix = filename.substring(filename.lastIndexOf("."));
            }
            if (StringUtils.isBlank(suffix)) {
                logger.warn(ConstantUtil.LOGGER_CONTROLLER_RESOURCE + method + "文件格式错误");
                return new ReturnStructure(400, "文件格式错误");
            }
            //获取请求user
            User user = hostHolder.getUser();
            System.out.println(user);
            if (user == null) {
                return new ReturnStructure(400, "上传文件失败!");
            }
            //生成随机文件名
            String saveName = UUIDUtil.generateUUID() + suffix;
            //文件存放路径
            File dest = new File(uploadPath + File.separator + saveName);
            try {
                file.transferTo(dest);
            } catch (IOException e) {
                logger.error("上传文件失败，" + e.getMessage(), e);
                return new ReturnStructure(400, "上传文件失败,服务器异常!");
            }
            // 缩略图文件名
            String picName;
            // 判断缩略图是pdf的还是视频的，并生成
            if (pdf.equals(suffix)){
                picName = CapturePdfUtil.generateBookIamge(uploadPath + File.separator + saveName,uploadPath + File.separator);
            }else{
                picName = CaptureVideoUtil.videoImage(uploadPath + File.separator + saveName, uploadPath + File.separator);
            }
            // 设置文件信息
            resource.setResourcename(filename);
            resource.setSize(filesize);
            //路径
            String addr = domain + contextPath + "/" + saveName;
            resource.setResourceaddress(addr);
            resource.setPictureaddress(domain + contextPath + "/" + picName);
            //文件后缀 去掉.
            resource.setType(suffix.substring(1));
            // 判断文件是否存储成功
            int row = resourceService.addResource(resource,user);
            if (row == 0) {
                logger.warn(ConstantUtil.LOGGER_CONTROLLER_RESOURCE + method + "文件上传失败，服务器异常！");
                return new ReturnStructure(400, "文件上传失败，服务器异常！");
            }

            Map<String, Object> map = new HashMap<>(5);
            map.put("id", resource.getId());
            return new ReturnStructure(200, "文件上传成功", map);

        }
    }

    /**
     * 请求下载文件
     *
     * @param fileId   文件id
     * @param response 响应
     */
    @OperationLogAnnotation(url = "/resource/download/{fileId}",modul = "资源模块",type = "下载",description = "下载资源文件")
    @ApiOperation(value = "下载文件接口", notes = "基于文件Id下载文件")
    @ApiImplicitParam(paramType = "path", name = "fileId", value = "文件ID", required = true, dataType = "Integer")
    @ApiResponse(code = 400, message = "请求失败", response = ReturnStructure.class)
    @GetMapping("/download/{fileId}")
    @CrossOrigin
    public ReturnStructure download(@PathVariable("fileId") int fileId, HttpServletResponse response, HttpServletRequest request) throws UnsupportedEncodingException {

        //根据fileId查询Resource
        Resource resource = resourceService.getResourceById(fileId);
        // 设置下载文件名
        String fileName = new String(resource.getResourcename().getBytes("GBK"), StandardCharsets.ISO_8859_1);
        response.addHeader( "Content-Disposition", "attachment;filename=" + fileName );
        //设置response类型
        String suffix = resource.getType();
        //设置返回内容类型，根据需要进行扩展
        switch (suffix) {
            case ConstantUtil.RESOURCE_TYPE_PNG:
            case ConstantUtil.RESOURCE_TYPE_JPG:
            case ConstantUtil.RESOURCE_TYPE_GIF:
                response.setContentType("image/" + suffix);
                break;
            case ConstantUtil.RESOURCE_TYPE_MP4:
                response.setContentType("video/" + suffix);
                break;
            case ConstantUtil.RESOURCE_TYPE_MP3:
                response.setContentType("audio/mpeg");
                break;
            case ConstantUtil.RESOURCE_TYPE_ZIP:
            case ConstantUtil.RESOURCE_TYPE_PDF:
                response.setContentType("audio/" + suffix);
                break;
            case ConstantUtil.RESOURCE_TYPE_PPT:
                response.setContentType("application/vnd.ms-powerpoint");
                break;
            default:
                break;
        }

        //文件地址 = 存放地址+Resource的addr后半段
        String fileAddr = resource.getResourceaddress();
        String[] splits = fileAddr.split("/");
        String saveAddr = uploadPath + File.separator + splits[splits.length - 1];

        //标志位
        //读取文件并通过response返回
        return getFileOutput(response, saveAddr);
    }

    /**
     * 请求播放资源
     *
     * @param fileId   文件id
     * @param response 响应
     */
    @ApiOperation(value = "播放文件接口", notes = "基于文件Id播放文件")
    @ApiImplicitParam(paramType = "path", name = "fileId", value = "文件ID", required = true, dataType = "Integer")
    @ApiResponse(code = 400, message = "请求失败", response = ReturnStructure.class)
    @GetMapping( "/display/{fileId}")
    @CrossOrigin
    public ReturnStructure display(@PathVariable("fileId") int fileId, HttpServletResponse response, HttpServletRequest request) throws UnsupportedEncodingException {

        //根据fileId查询Resource
        String pdf = "pdf";
        Resource resource = resourceService.getResourceById(fileId);
        if(!pdf.equals(resource.getType())){
            //设置进度条可拉
            //如果是video标签发起的请求就不会为null
            String rangeString = request.getHeader("Range");
            long range = Long.parseLong(rangeString.substring(rangeString.indexOf("=") + 1, rangeString.indexOf("-")));
            // 10000是视频文件的大小，上传文件时都会有这些参数的
            response.setContentLength(Integer.parseInt(resource.getSize()));
            //拖动进度条时的断点，其中10000是上面的视频文件大小，改成你的就好
            response.setHeader("Content-Range", String.valueOf(range + (Integer.parseInt(resource.getSize())-1)));
            response.setHeader("Accept-Ranges", "bytes");
        }
        // 设置下载文件名
        String fileName = new String(resource.getResourcename().getBytes("GBK"), StandardCharsets.ISO_8859_1);
        response.addHeader( "Content-Disposition", "attachment;filename=" + fileName );
        //设置response类型
        String suffix = resource.getType();
        //设置返回内容类型，根据需要进行扩展
        switch (suffix) {
            case ConstantUtil.RESOURCE_TYPE_PNG:
            case ConstantUtil.RESOURCE_TYPE_JPG:
            case ConstantUtil.RESOURCE_TYPE_GIF:
                response.setContentType("image/" + suffix);
                break;
            case ConstantUtil.RESOURCE_TYPE_MP4:
                response.setContentType("video/" + suffix);
                break;
            case ConstantUtil.RESOURCE_TYPE_MP3:
                response.setContentType("audio/mpeg");
                break;
            case ConstantUtil.RESOURCE_TYPE_ZIP:
            case ConstantUtil.RESOURCE_TYPE_PDF:
                response.setContentType("audio/" + suffix);
                break;
            case ConstantUtil.RESOURCE_TYPE_PPT:
                response.setContentType("application/vnd.ms-powerpoint");
                break;
            default:
                break;
        }

        //文件地址 = 存放地址+Resource的addr后半段
        String fileAddr = resource.getResourceaddress();
        String[] splits = fileAddr.split("/");
        String saveAddr = uploadPath + File.separator + splits[splits.length - 1];

        //标志位
        //读取文件并通过response返回
        return getFileOutput(response, saveAddr);
    }

    /**
     * 请求图片文件流
     *
     * @param fileId   文件id
     * @param response 响应
     */
    @ApiOperation(value = "展示图片接口", notes = "基于文件Id展示图片")
    @ApiImplicitParam(paramType = "path", name = "fileId", value = "文件ID", required = true, dataType = "Integer")
    @ApiResponse(code = 400, message = "请求失败", response = ReturnStructure.class)
    @GetMapping("/display/pic/{fileId}")
    @CrossOrigin
    public ReturnStructure displayPic(@PathVariable("fileId") int fileId, HttpServletResponse response) {

        //根据fileId查询Resource
        Resource resource = resourceService.getResourceById(fileId);
        //设置response类型
        String suffix = resource.getType();
        //设置返回内容类型，根据需要进行扩展
        switch (suffix) {
            case ConstantUtil.RESOURCE_TYPE_PNG:
            case ConstantUtil.RESOURCE_TYPE_JPG:
            case ConstantUtil.RESOURCE_TYPE_GIF:
                response.setContentType("image/" + suffix);
                break;
            default:
                break;
        }

        //文件地址 = 存放地址+Resource的addr后半段
        String fileAddr = resource.getPictureaddress();
        String[] splits = fileAddr.split("/");
        String saveAddr = uploadPath + File.separator+"pic" + File.separator + splits[splits.length - 1];

        //标志位
        //读取文件并通过response返回
        return getFileOutput(response, saveAddr);
    }

    /**
     * 读取文件并返回数据流
     * @param response 响应体
     * @param saveAddr 文件存储地址
     * @return 返回体
     */
    private ReturnStructure getFileOutput(HttpServletResponse response, String saveAddr) {
        // 获取文件输入流
        try (FileInputStream fis = new FileInputStream(saveAddr)) {
            // 获取响应输出流
            OutputStream os = response.getOutputStream();
            // 写入数据到输出流
            byte[] buffer = new byte[1024];
            int b;
            while ((b = fis.read(buffer)) != -1) {
                os.write(buffer, 0, b);
            }
            os.close();
        } catch (IOException e) {
            logger.error("下载文件失败" + e.getMessage(), e);
            return new ReturnStructure(400, "下载文件失败");
        }

        return new ReturnStructure(200, "下载文件成功");
    }


    /**
     * 查询resource数据总数
     *
     * @return JSON
     */
    @ApiOperation(value = "按条件查询数据总数接口")
    @ApiResponse(code = 400, message = "请求失败", response = ReturnStructure.class)
    @PostMapping("/queryCount")
    @ResponseBody
    @CrossOrigin
    public ReturnStructure queryCount(@RequestBody ResourceListQuery resourceListQuery) {
        // 按条件获取资源总数
        int count = resourceService.queryCount(resourceListQuery);
        Map<String, Object> map = new HashMap<>(5);
        map.put("count", count);
        return new ReturnStructure(200, "请求成功", map);
    }


    /**
     * 查询资源文件列表
     *
     * @param resourceListQuery 数据
     * @return JSON
     */
    @ApiOperation(value = "查询资源文件列表", notes = "基于当前页和每页数量进行分页查询")
    @ApiImplicitParam(paramType = "body", name = "resourceListQuery", value = "ResourceListQuery解析传入JSON", required = true, dataType = "ResourceListQuery")
    @ApiResponse(code = 400, message = "请求失败", response = ReturnStructure.class)
    @PostMapping("/queryList")
    @ResponseBody
    @CrossOrigin
    public ReturnStructure queryList(@RequestBody ResourceListQuery resourceListQuery) {
        String method = "/queryList：";
        if (resourceListQuery == null) {
            logger.warn(ConstantUtil.LOGGER_CONTROLLER_RESOURCE + method + "请求错误");
            return new ReturnStructure(400, "请求错误");

        } else if (resourceListQuery.getCurrentPage() == 0 || resourceListQuery.getPageSize() == 0) {
            logger.warn(ConstantUtil.LOGGER_CONTROLLER_RESOURCE + method + "请求错误");
            return new ReturnStructure(400, "请求错误");
        } else {
            String key = "resourceList";
            // 通过条件查找资源
            Map<String, Object> data = resourceService.queryByLimit(resourceListQuery);
            if (data == null || data.isEmpty() || data.get(key) == null) {
                logger.warn(ConstantUtil.LOGGER_CONTROLLER_RESOURCE + method + "请求错误");
                return new ReturnStructure(400, "请求错误");
            } else {
                return new ReturnStructure(200, "请求成功", data);
            }

        }
    }

    /**
     * 查找相关资源列表
     * @param query 查找条件
     * @return 相关资源列表
     */
    @ApiOperation(value = "查找相关资源列表", notes = "基于当前标签进行查询")
    @ApiImplicitParam(paramType = "body", name = "query", value = "传入JSON", required = true, dataType = "Map")
    @ApiResponse(code = 400, message = "请求失败", response = ReturnStructure.class)
    @ResponseBody
    @PostMapping("/queryRelatedResource")
    public ReturnStructure queryRelatedResource(@RequestBody Map<String,Object> query){
        int code;
        String message;
        String tags = "tags";
        String count = "count";
        String rid = "resourceid";
        Map<String, Object> map = new HashMap<>(0);
        if (query.get(tags) == null || query.get(tags) == ""){
            code = 404;
            message = "无tags参数或参数不全";
        }else if (query.get(count) != null && query.get(count) != "") {
            if ((query.get(rid) == null) || (query.get(rid) == "")){
                code = 404;
                message = "无resourceid参数或参数不全";
            }else{
                code = 200;
                // 根据标签获取相关资源列表数据
                List<Resource> relatedResources = resourceService.queryRelatedResource(query);
                message = "查询成功";
                map.put("relatedResourceList",relatedResources);
            }
        } else {
            code = 404;
            message = "无count参数或参数不全";
        }
        return new ReturnStructure(code,message,map);
    }

    /**
     * 获取热点资源列表
     * @param size 要获取的热点资源数量1
     * @return 热点资源列表
     */
    @ApiOperation(value = "获取热点资源列表", notes = "基于资源播放量进行查询")
    @ApiImplicitParam(paramType = "body", name = "size", value = "传入JSON", required = true, dataType = "Map")
    @ApiResponse(code = 400, message = "请求失败", response = ReturnStructure.class)
    @ResponseBody
    @PostMapping("/queryHotResource")
    public ReturnStructure queryHotResource(@RequestBody Map<String,Object> size){
        int code = 200;
        String message = "";
        Map<String, Object> map = new HashMap<>(0);
        // 判断参数是否为空
        String count = "pageSize";
        if(size.get(count) == null || size.get(count) == ""){
            return new ReturnStructure(400,"参数错误",null);
        }
        // 获取参数
        int pageSize = (int) size.get("pageSize");
        // 获取热点资源前pageSize条列表数据
        List<Resource> hotResources = resourceService.queryHotResource(pageSize);
        map.put("hotResourceList",hotResources);

        return new ReturnStructure(code,message,map);
    }

    /**
     * 请求删除资源
     *
     * @param map 数据
     * @return JSON
     */
    @OperationLogAnnotation(url = "/resource/deleteResource",modul = "资源模块",type = "删除",description = "根据资源id删除资源文件")
    @ApiOperation(value = "删除接口", notes = "根据传入Id删除资源")
    @ApiImplicitParam(paramType = "body", name = "map", value = "Map形式结合注解解析传入JSON", required = true, dataType = "Map")
    @ApiResponse(code = 400, message = "请求失败", response = ReturnStructure.class)
    @PostMapping("/deleteResource")
    @ResponseBody
    @CrossOrigin
    public ReturnStructure deleteResource(@RequestBody Map<String, Object> map) {
        String key = "id";
        String method = "/deleteResource：";
        if (map == null || map.isEmpty()) {
            logger.warn(ConstantUtil.LOGGER_CONTROLLER_RESOURCE + method + "请求错误");
            return new ReturnStructure(400, "请求错误");
        } else if (map.get(key) == null) {
            logger.warn(ConstantUtil.LOGGER_CONTROLLER_RESOURCE + method + "请求错误");
            return new ReturnStructure(400, "请求错误");
        } else {
            // 获取id
            Integer id = (Integer) map.get("id");
            // 判断删除操作是否成功
            int row = resourceService.deleteResource(id);
            if (row == 0) {
                logger.warn(ConstantUtil.LOGGER_CONTROLLER_RESOURCE + method + "请求错误");
                return new ReturnStructure(400, "请求错误");
            } else {
                return new ReturnStructure(200, "操作成功");
            }
        }
    }

    /**
     * 根据文件Id查询文件信息
     *
     * @param fileId 文件Id
     * @return JSON
     */
    @ApiOperation(value = "查询数据接口", notes = "基于Id查询数据")
    @ApiImplicitParam(paramType = "path", name = "fileId", value = "文件ID", required = true, dataType = "Integer")
    @ApiResponse(code = 400, message = "请求失败", response = ReturnStructure.class)
    @GetMapping("/getResourceById/{fileId}")
    @ResponseBody
    @CrossOrigin
    public ReturnStructure getResourceById(@PathVariable("fileId") int fileId) {
        // 根据id获取资源信息
        Resource resource = resourceService.getResourceById(fileId);
        if (resource == null) {
            return new ReturnStructure(400, "请求错误");
        } else {
            Map<String, Object> map = new HashMap<>(5);
            map.put("resource", resource);
            return new ReturnStructure(200, "查询成功", map);
        }

    }

}
